import json
import logging
from datetime import date
from typing import TypedDict
from django.db.models import F, CharField
from django.db.models.functions import Cast
from django.db import transaction
from django.views.decorators.http import require_http_methods
from administrators.models import Userinfo
from administrators.views.token_check import verify_token
from inventory.models import Handle, Record
from inventory.views.func.record import record_create, record_delete, record_update
from utils.enums import SCE
from utils.result import R
from utils.tools import ParamCheck

logger = logging.getLogger('inventory')


class RecordList(TypedDict):
    index: int  # 序号，表明在列表中的位置
    material_id: int
    whole_piece: int
    scatt_piece: int
    weight: str
    remark: str


@verify_token()
@require_http_methods(['POST'])
def record_list_crud(request, user_id: int):
    data = json.loads(request.body)
    user_obj = Userinfo.objects.get(id=user_id)
    match data['mode']:
        case 'create':
            in_out_list: list[RecordList] = data['param']['in_out_list']
            handle_id = data['param']['handle_id']
            actual_date = ParamCheck.date(data['param']['actual_date'])
            trend = data['param']['trend']
            try:
                handle_obj = Handle.objects.get(id=handle_id)
                return record_create(in_out_list, handle_obj, actual_date, trend, user_obj)
            except Handle.DoesNotExist:
                return R.error(SCE.no_enum, '处理方不存在').data()
            except Exception as e:
                print(e)
                return R.error(SCE.no_enum, '异常故障，请联系管理员').data()

        # 2025-08-11 审查完成
        case 'delete':
            record_id = ParamCheck.int(data['param']['id'])
            try:
                with transaction.atomic():
                    record_obj = Record.objects.get(id=record_id)
                    return record_delete(record_obj, user_obj)
            except Record.DoesNotExist:
                return R.error(SCE.no_enum, '记录不存在，请刷新后重新选择').data()
            except Exception as e:
                return R.error(SCE.no_enum, '异常故障，请联系管理员').data()

        case 'update':
            # 只允许更改以下三项
            record_id = ParamCheck.int(data['param']['record_id'])
            whole_piece = ParamCheck.int(data['param']['whole_piece'])
            scatt_piece = ParamCheck.int(data['param']['scatt_piece'])
            weight = ParamCheck.decimal(data['param']['weight'])
            remark = ParamCheck.str(data['param']['remark'], length=(0, 100))
            trend = ParamCheck.bool(data['param']['trend'])
            try:
                record_obj = Record.objects.get(id=record_id)
                result_update: bool | tuple[bool, date] = record_update(record_obj, whole_piece, scatt_piece, weight, remark, trend)
                if result_update is True:
                    return R.success('修改成功，请刷新页面').data()
                else:
                    return R.error(SCE.no_enum, f'修改记录导致 {result_update[1]} 日期库存非法').data()
            except Record.DoesNotExist:
                return R.error(SCE.no_enum, '要修改的记录不存在').data()

        case 'read':
            record_id = ParamCheck.int(data['param']['record_id'])
            result = (Record.objects
                      .filter(id=record_id)
                      .annotate(operator_name=F('operator__username'), handle_name=F('handle_name'),
                                material_code=F('material__code'),
                                material_name=F('material_name'), material_model=F('material_model'))
                      .values('id', 'trend', 'whole_piece', 'scatt_piece', 'weight', 'remark', 'handle_name',
                              'operator_name', 'material_code', 'material_name', 'material_model',
                              operate_date_=Cast('operate_date', CharField()),
                              actual_date_=Cast('actual_date', CharField()))
                      )
            return R.success('记录读取成功').data({result: list(result)})

        case _:
            return R.error(SCE.ParamError).data()


@verify_token()
@require_http_methods(['POST'])
def record_fuzzy_search(request, user_id: str):
    data = json.loads(request.body)
    # 可以按照以下四个外键进行归类
    material_group_id = ParamCheck.int(data['material_group_id'], force=False)
    handle_id = ParamCheck.int(data['handle_id'], force=False)
    handle_group_id = ParamCheck.int(data['handle_group_id'], force=False)
    material_id = ParamCheck.int(data['material_id'], force=False)
    # 允许按时间范围进行搜索
    operate_date = ParamCheck.date_range(data['operate_date'], force=False)
    actual_date = ParamCheck.date_range(data['actual_date'], force=False)
    # 出入库方向
    trend = ParamCheck.str(data['trend'])  # in out all

    start = ParamCheck.int(data['start'])
    stop = ParamCheck.int(data['stop'])

    result = Record.objects

    if trend is not None:
        if trend == 'in':
            result = result.filter(trend=False)
        if trend == 'out':
            result = result.filter(trend=True)
    if material_group_id is not None:
        result = result.filter(material__group_id=material_group_id)
    if material_id is not None:
        result = result.filter(material_id=material_id)
    if handle_id is not None:
        result = result.filter(handle_id=handle_id)
    if handle_group_id is not None:
        result = result.filter(handle__handle_group_id=handle_group_id)
    if operate_date is not None:
        result = result.filter(operate_date__gte=operate_date[0], operate_date__lte=operate_date[1])
    if actual_date is not None:
        result = result.filter(actual_date__gte=actual_date[0], actual_date__lte=actual_date[1])

    result = (result
              .annotate(operator_name=F('operator__username'), handle_name=F('handle__name'),
                        material_code=F('material__code'), set_piece=F('material__set_piece'),
                        material_name=F('material__name'), material_model=F('material__model'))
              .values('id', 'trend', 'whole_piece', 'scatt_piece', 'weight', 'remark', 'handle_name',
                      'operator_name', 'material_code', 'material_name', 'material_model', 'set_piece',
                      operate_date_=Cast('operate_date', CharField()),
                      actual_date_=Cast('actual_date', CharField()))
              .order_by('actual_date', 'trend')
              )

    total = result.count()
    result = result[start: stop]

    return R.success('记录模糊搜索成功').data({'result': list(result), 'total': total})
